2 * Adium is the legal property of its developers, whose names are listed in the copyright file included
3 * with this source distribution.
5 * This program is free software; you can redistribute it and/or modify it under the terms of the GNU
6 * General Public License as published by the Free Software Foundation; either version 2 of the License,
7 * or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
10 * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
11 * Public License for more details.
13 * You should have received a copy of the GNU General Public License along with this program; if not,
14 * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 #import <Adium/AIAccountControllerProtocol.h>
18 #import <Adium/AIStatusControllerProtocol.h>
19 #import "ESPurpleYahooAccount.h"
20 #import "ESPurpleYahooAccountViewController.h"
21 #import <AdiumLibpurple/SLPurpleCocoaAdapter.h>
22 #import <Adium/AIHTMLDecoder.h>
23 #import <Adium/AIListContact.h>
24 #import <Adium/AIStatus.h>
25 #import <Adium/ESFileTransfer.h>
26 #import <Libpurple/yahoo.h>
27 #import <Libpurple/yahoo_friend.h>
29 @implementation ESPurpleYahooAccount
31 - (const char*)protocolPlugin
36 - (void)configurePurpleAccount
38 [super configurePurpleAccount];
40 purple_account_set_string(account, "room_list_locale", [[self preferenceForKey:KEY_YAHOO_ROOM_LIST_LOCALE
41 group:GROUP_ACCOUNT_STATUS] UTF8String]);
43 //Make sure we're not turning japanese oh no not turning japanese I really think so
44 purple_account_set_bool(account, "yahoojp", FALSE);
48 * @brief The UID will be changed. The account has a chance to perform modifications
50 * Remove @yahoo.com from the proposedUID - a common mistake is to include it in the yahoo ID
52 * @param proposedUID The proposed, pre-filtered UID (filtered means it has no characters invalid for this servce)
53 * @result The UID to use; the default implementation just returns proposedUID.
55 - (NSString *)accountWillSetUID:(NSString *)proposedUID
59 if ((proposedUID && ([proposedUID length] > 0)) &&
60 ([proposedUID rangeOfString:@"@yahoo.com" options:NSCaseInsensitiveSearch].location != NSNotFound)) {
61 correctUID = [proposedUID substringToIndex:[proposedUID rangeOfString:@"@yahoo.com"
62 options:NSCaseInsensitiveSearch].location];
64 correctUID = proposedUID;
70 - (NSSet *)supportedPropertyKeys
72 static NSMutableSet *supportedPropertyKeys = nil;
74 if (!supportedPropertyKeys) {
75 supportedPropertyKeys = [[NSMutableSet alloc] initWithObjects:
79 [supportedPropertyKeys unionSet:[super supportedPropertyKeys]];
82 return supportedPropertyKeys;
86 * @brief Should set aliases serverside?
88 * Yahoo supports serverside aliases.
90 - (BOOL)shouldSetAliasesServerside
95 #pragma mark Connection
96 - (NSString *)connectionStringForStep:(int)step
101 return AILocalizedString(@"Connecting",nil);
107 - (BOOL)shouldAttemptReconnectAfterDisconnectionError:(NSString **)disconnectionError
109 BOOL shouldAttemptReconnect = YES;
111 if (disconnectionError && *disconnectionError) {
112 if ([*disconnectionError rangeOfString:@"Incorrect password"].location != NSNotFound) {
113 [self serverReportedInvalidPassword];
114 } else if (([*disconnectionError rangeOfString:@"You have signed on from another location"].location != NSNotFound) ||
115 ([*disconnectionError rangeOfString:@"logged in on a different machine or device"].location != NSNotFound)) {
116 shouldAttemptReconnect = NO;
120 return shouldAttemptReconnect;
123 #pragma mark Encoding
124 - (NSString *)encodedAttributedString:(NSAttributedString *)inAttributedString forListObject:(AIListObject *)inListObject
127 return [AIHTMLDecoder encodeHTML:inAttributedString
130 includingColorTags:YES
133 closeStyleTagsOnFontChange:YES
137 attachmentsAsText:YES
138 onlyIncludeOutgoingImages:NO
142 return [inAttributedString string];
146 #pragma mark File transfer
147 - (BOOL)canSendFolders
152 - (void)beginSendOfFileTransfer:(ESFileTransfer *)fileTransfer
154 [super _beginSendOfFileTransfer:fileTransfer];
158 - (void)acceptFileTransferRequest:(ESFileTransfer *)fileTransfer
160 [super acceptFileTransferRequest:fileTransfer];
163 - (void)rejectFileReceiveRequest:(ESFileTransfer *)fileTransfer
165 [super rejectFileReceiveRequest:fileTransfer];
168 - (void)cancelFileTransfer:(ESFileTransfer *)fileTransfer
170 [super cancelFileTransfer:fileTransfer];
173 #pragma mark Status Messages
176 * @brief Status name to use for a Purple buddy
178 - (NSString *)statusNameForPurpleBuddy:(PurpleBuddy *)buddy
180 NSString *statusName = nil;
181 PurplePresence *presence = purple_buddy_get_presence(buddy);
182 PurpleStatus *status = purple_presence_get_active_status(presence);
183 const char *purpleStatusID = purple_status_get_id(status);
185 if (!purpleStatusID) return nil;
187 if (!strcmp(purpleStatusID, YAHOO_STATUS_TYPE_BRB)) {
188 statusName = STATUS_NAME_BRB;
190 } else if (!strcmp(purpleStatusID, YAHOO_STATUS_TYPE_BUSY)) {
191 statusName = STATUS_NAME_BUSY;
193 } else if (!strcmp(purpleStatusID, YAHOO_STATUS_TYPE_NOTATHOME)) {
194 statusName = STATUS_NAME_NOT_AT_HOME;
196 } else if (!strcmp(purpleStatusID, YAHOO_STATUS_TYPE_NOTATDESK)) {
197 statusName = STATUS_NAME_NOT_AT_DESK;
199 } else if (!strcmp(purpleStatusID, YAHOO_STATUS_TYPE_NOTINOFFICE)) {
200 statusName = STATUS_NAME_NOT_IN_OFFICE;
202 } else if (!strcmp(purpleStatusID, YAHOO_STATUS_TYPE_ONPHONE)) {
203 statusName = STATUS_NAME_PHONE;
205 } else if (!strcmp(purpleStatusID, YAHOO_STATUS_TYPE_ONVACATION)) {
206 statusName = STATUS_NAME_VACATION;
208 } else if (!strcmp(purpleStatusID, YAHOO_STATUS_TYPE_OUTTOLUNCH)) {
209 statusName = STATUS_NAME_LUNCH;
211 } else if (!strcmp(purpleStatusID, YAHOO_STATUS_TYPE_STEPPEDOUT)) {
212 statusName = STATUS_NAME_STEPPED_OUT;
214 } else if (!strcmp(purpleStatusID, YAHOO_STATUS_TYPE_INVISIBLE)) {
215 statusName = STATUS_NAME_INVISIBLE;
222 * @brief Status message for a contact
224 - (NSAttributedString *)statusMessageForPurpleBuddy:(PurpleBuddy *)b
226 NSString *statusMessageString = nil;
227 NSAttributedString *statusMessage = nil;
228 char *normalized = g_strdup(purple_normalize(b->account, b->name));
229 struct yahoo_data *od;
232 if ((purple_account_is_connected(account)) &&
233 (od = account->gc->proto_data) &&
234 (f = g_hash_table_lookup(od->friends, normalized))) {
236 if (f->msg != NULL) {
237 statusMessageString = [NSString stringWithUTF8String:f->msg];
239 } else if (f->status != YAHOO_STATUS_AVAILABLE) {
241 case YAHOO_STATUS_BRB:
242 statusMessageString = [[adium statusController] localizedDescriptionForCoreStatusName:STATUS_NAME_BRB];
245 case YAHOO_STATUS_BUSY:
246 statusMessageString = [[adium statusController] localizedDescriptionForCoreStatusName:STATUS_NAME_BUSY];
249 case YAHOO_STATUS_NOTATHOME:
250 statusMessageString = [[adium statusController] localizedDescriptionForCoreStatusName:STATUS_NAME_NOT_AT_HOME];
253 case YAHOO_STATUS_NOTATDESK:
254 statusMessageString = [[adium statusController] localizedDescriptionForCoreStatusName:STATUS_NAME_NOT_AT_DESK];
257 case YAHOO_STATUS_NOTINOFFICE:
258 statusMessageString = [[adium statusController] localizedDescriptionForCoreStatusName:STATUS_NAME_NOT_IN_OFFICE];
261 case YAHOO_STATUS_ONPHONE:
262 statusMessageString = [[adium statusController] localizedDescriptionForCoreStatusName:STATUS_NAME_PHONE];
265 case YAHOO_STATUS_ONVACATION:
266 statusMessageString = [[adium statusController] localizedDescriptionForCoreStatusName:STATUS_NAME_VACATION];
269 case YAHOO_STATUS_OUTTOLUNCH:
270 statusMessageString = [[adium statusController] localizedDescriptionForCoreStatusName:STATUS_NAME_LUNCH];
273 case YAHOO_STATUS_STEPPEDOUT:
274 statusMessageString = [[adium statusController] localizedDescriptionForCoreStatusName:STATUS_NAME_STEPPED_OUT];
277 case YAHOO_STATUS_INVISIBLE:
278 statusMessageString = [[adium statusController] localizedDescriptionForCoreStatusName:STATUS_NAME_INVISIBLE];
279 // statusType = AIInvisibleStatusType; /* Invisible has a special status type */
282 case YAHOO_STATUS_AVAILABLE:
283 case YAHOO_STATUS_WEBLOGIN:
284 case YAHOO_STATUS_CUSTOM:
285 case YAHOO_STATUS_IDLE:
286 case YAHOO_STATUS_OFFLINE:
287 case YAHOO_STATUS_TYPING:
292 if (statusMessageString) {
293 statusMessage = [[[NSAttributedString alloc] initWithString:statusMessageString
294 attributes:nil] autorelease];
300 return statusMessage;
304 * @brief Update the status message and away state of the contact
306 - (void)updateStatusForContact:(AIListContact *)theContact toStatusType:(NSNumber *)statusTypeNumber statusName:(NSString *)statusName statusMessage:(NSAttributedString *)statusMessage
308 NSString *statusMessageString = [statusMessage string];
309 char *normalized = g_strdup(purple_normalize(account, [[theContact UID] UTF8String]));
310 struct yahoo_data *od;
313 /* Grab the idle time while we have a chance */
314 if ((purple_account_is_connected(account)) &&
315 (od = account->gc->proto_data) &&
316 (f = g_hash_table_lookup(od->friends, normalized))) {
318 if (f->status == YAHOO_STATUS_IDLE) {
324 idleSince = [NSDate dateWithTimeIntervalSinceNow:-idle];
326 idleSince = [NSDate date];
329 [theContact setStatusObject:idleSince
333 } else if (f->status == YAHOO_STATUS_INVISIBLE) {
334 statusTypeNumber = [NSNumber numberWithInt:AIInvisibleStatusType]; /* Invisible has a special status type */
340 //Yahoo doesn't have an explicit mobile state; instead the status message is automatically set to indicate mobility.
341 if (statusMessageString && ([statusMessageString isEqualToString:@"I'm on SMS"] ||
342 ([statusMessageString rangeOfString:@"I'm mobile"].location != NSNotFound))) {
343 [theContact setIsMobile:YES notify:NotifyLater];
345 } else if ([theContact isMobile]) {
346 [theContact setIsMobile:NO notify:NotifyLater];
349 [super updateStatusForContact:theContact
350 toStatusType:statusTypeNumber
351 statusName:statusName
352 statusMessage:statusMessage];
356 * @brief Return the purple status ID to be used for a status
358 * Most subclasses should override this method; these generic values may be appropriate for others.
360 * Active services provided nonlocalized status names. An AIStatus is passed to this method along with a pointer
361 * to the status message. This method should handle any status whose statusNname this service set as well as any statusName
362 * defined in AIStatusController.h (which will correspond to the services handled by Adium by default).
363 * It should also handle a status name not specified in either of these places with a sane default, most likely by loooking at
364 * [statusState statusType] for a general idea of the status's type.
366 * @param statusState The status for which to find the purple status ID
367 * @param arguments Prpl-specific arguments which will be passed with the state. Message is handled automatically.
369 * @result The purple status ID
371 - (const char *)purpleStatusIDForStatus:(AIStatus *)statusState
372 arguments:(NSMutableDictionary *)arguments
374 const char *statusID = NULL;
375 NSString *statusName = [statusState statusName];
376 NSString *statusMessageString = [statusState statusMessageString];
378 if (!statusMessageString) statusMessageString = @"";
380 switch ([statusState statusType]) {
381 case AIAvailableStatusType:
382 statusID = YAHOO_STATUS_TYPE_AVAILABLE;
385 case AIAwayStatusType:
387 if (([statusName isEqualToString:STATUS_NAME_BRB]) ||
388 ([statusMessageString caseInsensitiveCompare:[[adium statusController] localizedDescriptionForCoreStatusName:STATUS_NAME_BRB]] == NSOrderedSame))
389 statusID = YAHOO_STATUS_TYPE_BRB;
391 else if (([statusName isEqualToString:STATUS_NAME_BUSY]) ||
392 ([statusMessageString caseInsensitiveCompare:[[adium statusController] localizedDescriptionForCoreStatusName:STATUS_NAME_BUSY]] == NSOrderedSame))
393 statusID = YAHOO_STATUS_TYPE_BUSY;
395 else if (([statusName isEqualToString:STATUS_NAME_NOT_AT_HOME]) ||
396 ([statusMessageString caseInsensitiveCompare:[[adium statusController] localizedDescriptionForCoreStatusName:STATUS_NAME_NOT_AT_HOME]] == NSOrderedSame))
397 statusID = YAHOO_STATUS_TYPE_NOTATHOME;
399 else if (([statusName isEqualToString:STATUS_NAME_NOT_AT_DESK]) ||
400 ([statusMessageString caseInsensitiveCompare:[[adium statusController] localizedDescriptionForCoreStatusName:STATUS_NAME_NOT_AT_DESK]] == NSOrderedSame))
401 statusID = YAHOO_STATUS_TYPE_NOTATDESK;
403 else if (([statusName isEqualToString:STATUS_NAME_PHONE]) ||
404 ([statusMessageString caseInsensitiveCompare:[[adium statusController] localizedDescriptionForCoreStatusName:STATUS_NAME_PHONE]] == NSOrderedSame))
405 statusID = YAHOO_STATUS_TYPE_ONPHONE;
407 else if (([statusName isEqualToString:STATUS_NAME_VACATION]) ||
408 ([statusMessageString caseInsensitiveCompare:[[adium statusController] localizedDescriptionForCoreStatusName:STATUS_NAME_VACATION]] == NSOrderedSame))
409 statusID = YAHOO_STATUS_TYPE_ONVACATION;
411 else if (([statusName isEqualToString:STATUS_NAME_LUNCH]) ||
412 ([statusMessageString caseInsensitiveCompare:[[adium statusController] localizedDescriptionForCoreStatusName:STATUS_NAME_LUNCH]] == NSOrderedSame))
413 statusID = YAHOO_STATUS_TYPE_OUTTOLUNCH;
415 else if (([statusName isEqualToString:STATUS_NAME_STEPPED_OUT]) ||
416 ([statusMessageString caseInsensitiveCompare:[[adium statusController] localizedDescriptionForCoreStatusName:STATUS_NAME_STEPPED_OUT]] == NSOrderedSame))
417 statusID = YAHOO_STATUS_TYPE_STEPPEDOUT;
423 case AIInvisibleStatusType:
424 statusID = YAHOO_STATUS_TYPE_INVISIBLE;
427 case AIOfflineStatusType:
431 //If we didn't get a purple status ID, request one from super
432 if (statusID == NULL) statusID = [super purpleStatusIDForStatus:statusState arguments:arguments];
437 - (BOOL)shouldAddMusicalNoteToNowPlayingStatus
442 #pragma mark Contact List Menu Items
443 - (NSString *)titleForContactMenuLabel:(const char *)label forContact:(AIListContact *)inContact
445 if (!strcmp(label, "Add Buddy")) {
446 //We handle Add Buddy ourselves
449 } else if (!strcmp(label, "Join in Chat")) {
450 return [NSString stringWithFormat:AILocalizedString(@"Join %@'s Chat",nil),[inContact formattedUID]];
452 } else if (!strcmp(label, "Initiate Conference")) {
453 return [NSString stringWithFormat:AILocalizedString(@"Initiate Conference with %@",nil), [inContact formattedUID]];
455 } else if (!strcmp(label, "Presence Settings")) {
456 return [NSString stringWithFormat:AILocalizedString(@"Presence Settings for %@",nil), [inContact formattedUID]];
458 } else if (!strcmp(label, "Appear Online")) {
459 return [NSString stringWithFormat:AILocalizedString(@"Appear Online to %@",nil), [inContact formattedUID]];
461 } else if (!strcmp(label, "Appear Offline")) {
462 return [NSString stringWithFormat:AILocalizedString(@"Appear Offline to %@",nil), [inContact formattedUID]];
464 } else if (!strcmp(label, "Appear Permanently Offline")) {
465 return [NSString stringWithFormat:AILocalizedString(@"Always Appear Offline to %@",nil), [inContact formattedUID]];
467 } else if (!strcmp(label, "Don't Appear Permanently Offline")) {
468 return [NSString stringWithFormat:AILocalizedString(@"Don't Always Appear Offline to %@",nil), [inContact formattedUID]];
470 } else if (!strcmp(label, "View Webcam")) {
471 //return [NSString stringWithFormat:AILocalizedString(@"View %@'s Webcam",nil), [inContact formattedUID]];
474 } else if (!strcmp(label, "Start Doodling")) {
478 return [super titleForContactMenuLabel:label forContact:inContact];
481 #pragma mark Account Action Menu Items
482 - (NSString *)titleForAccountActionMenuLabel:(const char *)label
484 /* The Yahoo actions are "Activate ID" (or perhaps "Active ID," depending on where in the code you look)
485 * and "Join User in Chat...". These are dumb. Additionally, Join User in Chat doesn't work as of purple 1.1.4. */